require"node.decorator"
require"node.state"
require"node.prototype"


nodesbyguid = nodesbyguid or weaktable()
nodesbyname = nodesbyname or weaktable()

function getnodebyguid(guid) --> Unique
  return nodesbyguid[guid]
end

function getnodesbyname(name) --> List of nodes
  local nodes= {}
  local n
  local name = tolower(name)
  for n,_ in pairs(nodesbyname[name]) do
    table.insert(nodes,n)
  end
  return nodes
end

local getguid = getguid or function()
  __guid = __guid or 0
  __guid = __guid + 1
  return __guid
end

module(...,package.seeall)
local node_mt = {
  __name = "noname",
  __type = "node",
  __guid = -1,
  --__data = { },  
  --states        = state.newsystem(),
  --decorators    = decorator.newsystem(),
  --associations  = association.newsystem(),
  --prototypes    = {},
  is = function(self,test)
    if self.__guid == tonumber(test) then 
      return true
    elseif (type(test) == "string") and (self.__name == tolower(test)) then
      return true
    elseif has(self.decoratorsystem.decorators,test) then 
      return true
    elseif has(self.prototypes,test) then 
      return true
    end
    return false
  end,
  backup = function(self)
    -->create and return a memento of myself
    local m = {}
    m.__name = self.__name
    m.__guid = self.__guid
    m.decorators = {}
    local k,v
    for k,v in ipairs(self.decorators.decorators or {}) do
      table.insert(m.decorators,v)
    end

    m.statestack = {}
    for k,v in ipairs(self.states.statestack or {}) do
      table.insert(m.statestack,v)
    end
    m.prototypes = {}
    for k,v in ipairs(self.prototypes or {}) do
      table.insert(m.prototypes,v)
    end
    m.data = {}
    for k,v in pairs(self.__data or {}) do
      m.data[k] = v
    end    
    return readonly(m)
  end,
  rollback = function(self,memento)
    self.__name = memento.__name
    self.__guid = memento.__guid
    local k,v
    self.decorators.decorators ={}
    for k,v in ipairs(memento.decorators) do
      table.insert(self.decorators.decorators,v)
    end
    self.states.statestack ={}
    for k,v in ipairs(memento.statestack) do
      table.insert(self.states.statestack,v)
    end
    self.prototypes ={}
    for k,v in ipairs(memento.prototypes) do
      table.insert(self.prototypes,v)
    end       
    self.__data ={}
    for k,v in pairs(memento.data) do
      self.__data[k] = v
    end     
  end,
  handle = function(self,event,...)
    event = tolower(event)
    self.states:handle(event,...)
    local children = self.associations:getassociatedtargets("child")
    local c
    for c in pairs(children) do
      c.states:handle(event,...)
    end     
  end,
  __index = function (t,k)
    local v = getmetatable(t)[k]
    if v then return v end
    printd("*access to element " .. tostring(k))
    v = t.__data[k] or node.prototype.search(t.prototypes,k)
    printd("found k")
    local ds = t.decorators
    return ds:get(t,k,v)  -- access the original table
  end,
  __newindex = function (t,k,v)
    printd("*update of element " .. tostring(k) ..
                         " to " .. tostring(v))
    --assert(validatefield(t,k),"Field " .. k .. " not found in node " .. tostring(t))-- .. " of class " .. t.__classlist)
    --assert(validatefieldtype(k,v),"Invalid type")
    local valid = t[k] or t.__data[k] or node.prototype.search(t.prototypes,k) or getmetatable(t)[k]
    if valid then                   
      local ds = t.decorators
      t.__data[k] = ds:set(t,k,v)  -- update original table
    else
      error("attempt to set a non valid field", 2)
    end    
  end,  
  __tostring = function(self) 
    return format("NODE(name=%s;guid=%s)",self.__name,self.__guid) 
  end
}

factory = factory or {}

function factory.simple(name)
  local n = {}
  n.__name = name
  n.__data = { }
  n.__guid = getguid() -->Give the node a guid if it doesn't have one  
  n.prototypes={}
  n.associations  = association.newsystem()
  n.states        = state.newsystem()
  n.decorators    = decorator.newsystem()
  setmetatable(n,node_mt)
  return n
end

function factory.clone(node)
  printd("cloning "..tostring(node))
  local n = factory.simple(node.__name)
  local memento = node:backup()
  n:rollback(memento)
  return n  
end

function new(factory,...)
  local err, n = pcall(factory,...) -->Ask factory for an appropriate node
  assert(err,"createnode:"..tostring(err))
  nodesbyguid[n.__guid]=n -->store the node by guid
  local name = tolower(n.__name)
  assert(name,"created a nameless node "..tostring(n))
  nodesbyname[name] = nodesbyname[name] or weaktable()
  nodesbyname[name][n]=n  -->store the node by name
  return n  
end
